package cn.szzsi.common.oauthfilter;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;


import javax.net.ssl.SSLContext;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import cn.szzsi.AConfig;
import cn.szzsi.business.service.LoginService;
import cn.szzsi.model.Member;
import com.jfinal.kit.PropKit;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

public class OAuthFilter implements Filter{
	private static final Logger LOG = LoggerFactory.getLogger(OAuthFilter.class);

	private String authServerWebUrl = AConfig.p.get("authServerWebUrl");
	private String authServerApiUrl = AConfig.p.get("authServerApiUrl");
	private String appId = AConfig.p.get("appId");
	private String appSecret = AConfig.p.get("appSecret");

	private String sslTrustStore = "";
	private String sslPassword = "";

	private Executor executor;

	private JSONObject accessToken;

	private LoginService loginService;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		LayeredConnectionSocketFactory sslsf = null;
		try {
			String keystorePath = OAuthFilter.class.getResource(sslTrustStore).getFile();
			SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(new File(keystorePath),
					sslPassword.toCharArray(), new TrustSelfSignedStrategy()).build();
			sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
					SSLConnectionSocketFactory.getDefaultHostnameVerifier());
		} catch (final Exception ex) {
			LOG.warn(null, ex);
			sslsf = SSLConnectionSocketFactory.getSystemSocketFactory();
		}
		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

		executor = Executor.newInstance(httpclient);
	}

	/**
	 * @param request
	 * @param response
	 * @param chain
	 * @throws IOException
	 * @throws ServletException
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		HttpSession session = httpRequest.getSession();
		JSONObject userInfo = (JSONObject) session.getAttribute(Member.LOGIN_USER_INFO);
		if (userInfo != null){
			chain.doFilter(httpRequest, httpResponse);
			return;
		}
		String code = request.getParameter("code");
		if (StringUtils.isEmpty(code)){
			httpResponse.sendRedirect(getWebAuthUrl(httpRequest));
			return;
		}
		accessToken = getAccessToken(code);
		if (accessToken == null){
			httpResponse.sendRedirect(getWebAuthUrl(httpRequest));
			return;
		}
		userInfo = getUserInfo();
		session.setAttribute(Member.LOGIN_USER_INFO, userInfo);//
		session.setAttribute(Member.LOGOUT_URL, buildLogoutUrl(httpRequest.getRequestURL().toString(), session.getId()));
		loginService.loginSuccess(httpRequest,httpResponse, userInfo,code);

		chain.doFilter(httpRequest, httpResponse);
	}

	@Override
	public void destroy() {
	}

	public String getWebAuthUrl(HttpServletRequest request){
		StringBuffer param = new StringBuffer(authServerWebUrl);
		String trueUrl = request.getParameter("trueUrl");
		if (StringUtils.isEmpty(trueUrl)){
			StringBuffer redirectUri = request.getRequestURL();
			if(request.getQueryString()!= null){
				redirectUri.append("?" + request.getQueryString());
			}
			trueUrl = redirectUri.toString();
		}
		try {
			param.append("?redirect_uri=").append(URLEncoder.encode(trueUrl,"UTF-8"));
		} catch (UnsupportedEncodingException e) {
			// 不会发生吧
			LOG.error(null, e);
		}
		param.append("&state=").append(request.getSession().getId());
		param.append("&appid=").append(appId);
		param.append("&scope=").append("web_login");
		return param.toString();
	}

	public String buildLogoutUrl(String redirectUri, String state) {
		try {
			return uriBuilder("").addParameter("appid", appId)
					.addParameter("redirect_uri", redirectUri).addParameter("scope", "web_logout")
					.addParameter("state", state).build().toString();
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	private URIBuilder uriBuilder(String path) {
		try {
			return new URIBuilder(authServerWebUrl + path).setCharset(getCharset());
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	private JSONObject getAccessToken(String code) throws IOException {
		try {
			URI uri = apiUriBuilder("accessToken").addParameter("appid", appId)
					.addParameter("secret", appSecret).addParameter("code", code)
					.addParameter("grant_type", "authorization_code").addParameter("return_type", "json").build();
			return execute(Request.Get(uri), JSONObject.class);
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	public JSONObject getUserInfo() throws IOException {
		try {
			URI uri = apiUriBuilder("userInfo").addParameter("access_token", accessToken.getString("access_token"))
					.addParameter("openid", accessToken.getString("openid")).build();
			JSONObject response = execute(Request.Get(uri), JSONObject.class);
			return response;
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	private <T> T execute(Request request, Class<T> responseType) throws IOException {
		JSONObject response = JSONObject.parseObject(executor.execute(request).returnContent().asString(getCharset()));
		String responseCode = response.getString("code");
		if ("0000".equals(responseCode)) {
			return response.getObject("data", responseType);
		}
		LOG.info("调用认证服务器接口错误: {}", response);
		return null;
	}

	private URIBuilder apiUriBuilder(String path) {
		try {
			return new URIBuilder(authServerApiUrl + path).setCharset(getCharset());
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	private static Charset getCharset(){
		return Charset.forName("UTF-8");
	}

}
